﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using System.Runtime.InteropServices;
using System.Globalization;
using System.Windows.Threading;

namespace StudioEditorTest.CodeEditor
{
	/// <summary>
	/// Interaction logic for CodeBlockPresenter.xaml
	/// </summary>
	public partial class CodeBlockPresenter : UserControl
	{
		public CodeBlockPresenter()
		{
			InitializeComponent();

			this.Text = string.Empty;
			this.cursorBlinkTimer = new DispatcherTimer();
			this.cursorBlinkTimer.Tick += new EventHandler(cursorBlinkTimer_Tick);
			this.cursorBlinkTimer.Interval = TimeSpan.FromMilliseconds(700);
			this.cursorBlinkTimer.Start();
		}
	

		// Using a DependencyProperty as the backing store for CursorPosition.  This enables animation, styling, binding, etc...
		public static readonly DependencyProperty CursorPositionProperty =
			DependencyProperty.Register("CursorPosition", typeof(int), typeof(CodeBlockPresenter), new UIPropertyMetadata(0, null, CoerceCursorPositionCallback));

		// Using a DependencyProperty as the backing store for Active.  This enables animation, styling, binding, etc...
		public static readonly DependencyProperty ActiveProperty =
			DependencyProperty.Register("Active", typeof(bool), typeof(CodeBlockPresenter), new UIPropertyMetadata(false));

		// Using a DependencyProperty as the backing store for Text.  This enables animation, styling, binding, etc...
		public static readonly DependencyProperty TextProperty =
			DependencyProperty.Register("Text", typeof(string), typeof(CodeBlockPresenter), new UIPropertyMetadata(string.Empty));

		public static Object CoerceCursorPositionCallback(DependencyObject d,Object baseValue)
		{
			CodeBlockPresenter cb = d as CodeBlockPresenter;
			if (cb == null) return baseValue;
			return cb.ValidateCursorPosition((int)baseValue);
		}

		private void cursorBlinkTimer_Tick(object sender, EventArgs e)
		{
			showCursor = !showCursor;
			this.InvalidateVisual();
		}

		internal int ValidateCursorPosition(int source)
		{
			if (PositionChanged != null) PositionChanged(this, new EventArgs());

			if (source < 0) source = 0;
			else if(source > this.Text.Length) source = this.Text.Length;
			return source;
		}

		internal void ProcessKeyDown(KeyEventArgs e)
		{
			e.Handled = true;
			switch (e.Key)
			{
				case Key.Back:
				case Key.Delete:
					DeleteText(e.Key == Key.Back ? LogicalDirection.Backward : LogicalDirection.Forward);
					break;
				case Key.Left:
					this.CursorPosition--;
					break;
				case Key.Right:
					this.CursorPosition++;
					break;
				default:
					e.Handled = false;
					break;
			}
			if (!e.Handled)
			{
				string result = e.GetChar();
				if (!string.IsNullOrEmpty(result))
				{
					e.Handled = true;
					InsertText(LogicalDirection.Forward, result);
				}
			}

			if (e.Handled)
			{
				showCursor = true;
				this.InvalidateVisual();
			}
		}

		protected override void OnRender(DrawingContext drawingContext)
		{
			Typeface typeFace = new Typeface("Arial");
			FormattedText fText = new FormattedText(
				this.Text,
				CultureInfo.CurrentCulture,
				FlowDirection.LeftToRight,
				typeFace,
				14,
				Brushes.Black);
			
			drawingContext.DrawText(fText,new Point(0,0));

			if (showCursor)
			{
				Size position = GetCursorVisualPosition();
				drawingContext.DrawLine(new Pen(Brushes.Black, 0.8), new Point(position.Width, 0), new Point(position.Width, position.Height));
			}
		}

		private Size GetCursorVisualPosition()
		{
			Typeface typeFace = new Typeface("Arial");
			FormattedText fText = new FormattedText(
				this.Text.Substring(0,this.CursorPosition),
				CultureInfo.CurrentCulture,
				FlowDirection.LeftToRight,
				typeFace,
				14,
				Brushes.Black);
			return new Size(fText.WidthIncludingTrailingWhitespace,fText.Height);
		}

		protected override void OnPropertyChanged(DependencyPropertyChangedEventArgs e)
		{
			if (e.Property == CodeBlockPresenter.ActiveProperty && (bool)e.NewValue)
			{

			}
			base.OnPropertyChanged(e);
		}

		public int CursorPosition
		{
			get { return (int)GetValue(CursorPositionProperty); }
			set { SetValue(CursorPositionProperty, value); }
		}

		public bool Active
		{
			get { return (bool)GetValue(ActiveProperty); }
			set { SetValue(ActiveProperty, value); }
		}

		public string Text
		{
			get { return (string)GetValue(TextProperty); }
			set { SetValue(TextProperty, value); }
		}


		private void InsertText(LogicalDirection direction, string insertText)
		{
			int insertOffsetPosition = this.CursorPosition;
			int insertTextLenght = insertText.Length;
			string result = this.Text.Substring(0, insertOffsetPosition);
			result += insertText;
			result += this.Text.Substring(insertOffsetPosition, this.Text.Length - insertOffsetPosition);
			this.Text = result;

			if (direction == LogicalDirection.Forward) this.CursorPosition += insertTextLenght;
		}

		private void DeleteText(LogicalDirection direction)
		{
			int deleteOffsetPosition = this.CursorPosition;
			int deleteTextLenght = 1;

			string result = this.Text;
			if (direction == LogicalDirection.Forward)
			{
				result = this.Text.Substring(0, deleteOffsetPosition);
				result += this.Text.Substring(deleteOffsetPosition + deleteTextLenght);
			}
			else
			{
				result = this.Text.Substring(0, deleteOffsetPosition - deleteTextLenght);
				result += this.Text.Substring(deleteOffsetPosition);
				this.CursorPosition -= deleteTextLenght;
			}

			this.Text = result;
		}

		public event EventHandler PositionChanged;

		private DispatcherTimer cursorBlinkTimer;
		private bool showCursor;
	}
}
